/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package damas.logicaYcontroladores;

import java.util.LinkedList;
import java.util.List;

import javax.swing.SwingWorker;

import utils.Pair;
import damas.modelo.DamasEstado;
import damas.modelo.DamasModelo;
import framework.AdversarySearchEngine;

/**
 * Clase encargada de administrar la l�gica del juego
 * 
 * @author Sim�n Emmanuel Guti�rrez Brida
 * @version 0.1
 *          <hr>
 *          M�todos a implementar
 * @see DamasLogica#verificarMovidaEnEstado(utils.Pair, utils.Pair)
 */
public class DamasLogica {
	private DamasModelo modelo;
	private AdversarySearchEngine<DamasEstado> ia;
	final int delay = 800;
	private int turno;
	private boolean jugando;
	private Pair<Integer, Integer> inicioObligatio;
	private boolean generoMovObligatorio = false;

	/**
	 * Constructor de la clase
	 * 
	 * @param modelo
	 *            : el modelo del juego, contiene tambi�n el estado utilizado en
	 *            el problema de b�squeda : {@code DamasModelo}
	 * @param ia
	 *            : el motor de b�squeda para poder obtener el siguiente
	 *            movimiento : {@code AdversarySearchEngine<DamasEstado> ia}
	 */
	public DamasLogica(DamasModelo modelo, AdversarySearchEngine<DamasEstado> ia) {
		this.modelo = modelo;
		this.ia = ia;
		this.turno = 0;
		this.jugando = false;
	}

	/**
	 * Inicializa la gui y calcula el siguiente movimiento por si el jugador 1
	 * es ia
	 */
	private void iniciarJuego() {
		this.jugando = true;
		this.modelo.startGUI();
		siguienteMovimiento();
	}

	/**
	 * Inicia una nueva partida
	 */
	public void nuevoJuego() {

		this.turno = 0;
		if (this.obtenerColorJugador(1) == -3) {
			this.turno = 1;
		}
		this.modelo.definirEstado(new DamasEstado());
		this.modelo.deshabilitarPreferencias();
		iniciarJuego();
	}

	/**
	 * Mueva una ficha desde su posici�n original hasta el destino
	 * 
	 * @param inicio
	 *            : la posici�n de la ficha a mover :
	 *            {@code Pair<Integer,Integer>}
	 * @param destino
	 *            : la posici�n a la cual se mueve :
	 *            {@code Pair<Integer,Integer>}
	 *            <hr>
	 *            solo se cambia de turno si no es posible seguir comiendo con
	 *            la misma ficha
	 */
	public void mover(Pair<Integer, Integer> inicio,
			Pair<Integer, Integer> destino) {
		if (generoMovObligatorio) {
			inicio = inicioObligatio;
		}
		if (movidaValida(inicio, destino)) {
			boolean continua = this.modelo.mover(inicio, destino);
			if (continua) {
				generoMovObligatorio = true;
				inicioObligatio = destino;
			} else {
				generoMovObligatorio = false;
			}
			this.modelo.update();
			if (!continua) {
				turno = 1 - turno;
			}
			if (!this.modelo.juegoTermino())
				siguienteMovimiento();
		} else {
			this.modelo.update(); // deshace la movida incorrecta
		}
	}

	/**
	 * Retorna si un movimiento es v�lido
	 * 
	 * @param inicio
	 *            : la posici�n de la ficha a mover :
	 *            {@code Pair<Integer,Integer>}
	 * @param destino
	 *            : la posici�n a la cual se mueve :
	 *            {@code Pair<Integer,Integer>}
	 * @return true sii la movida es v�lida
	 *         <hr>
	 *         puntos a tener en cuenta:
	 *         <li>hay una ficha en la posici�n de inicio</li>
	 *         <li>el color de la ficha se corresponde al color del jugador
	 *         actual</li>
	 *         <li>es una movida v�lida</li>
	 *         <li>se est� comiendo una ficha de forma correcta</li>
	 *         <li>comidas consecutivas se hacen con la misma ficha</li>
	 */
	public boolean movidaValida(Pair<Integer, Integer> inicio,
			Pair<Integer, Integer> destino) {
		if (this.modelo.fichaEn(inicio) == this.modelo.colorJugador(turno)
				|| this.modelo.fichaEn(inicio) == this.modelo
						.colorJugador(turno) * modelo.DAMA) {
			// solo se verific� si la ficha pertenece al jugador
			return verificarMovidaEnEstado(inicio, destino);
		} else {
			return false;
		}
	}

	/**
	 * Verifica una movida utilizando datos del estado (DamasEstado)
	 * 
	 * @param inicio
	 *            : la posici�n de la ficha a mover :
	 *            {@code Pair<Integer,Integer>}
	 * @param destino
	 *            : la posici�n a la cual se mueve :
	 *            {@code Pair<Integer,Integer>}
	 * @return true sii la movida es v�lida
	 */
	private boolean verificarMovidaEnEstado(Pair<Integer, Integer> inicio,
			Pair<Integer, Integer> destino) {

		DamasEstado estado = modelo.obtenerEstado();
		int miColor = this.obtenerColorJugador(this.turno);
		List<Pair<Integer, Integer>> fichasPosiblesAComer = new LinkedList<Pair<Integer, Integer>>();
		if (miColor == modelo.ROJA) {
			// todas mis fichas con posibilidades a comer que tengo
			fichasPosiblesAComer = estado.getRojasObligadasAComer();
			fichasPosiblesAComer.addAll(estado.getRojasDamasObligadasAComer());

		} else {
			fichasPosiblesAComer = estado.getNegrasObligadasAComer();
			fichasPosiblesAComer.addAll(estado.getNegrasDamasObligadasAComer());
		}

		// true ssi existe una ficha en destino
		boolean hayFichaEnDestino = modelo.fichaEn(destino) != 0;
		if (hayFichaEnDestino)
			return false;// no puede hacer el moviemiento
		else {
			// ultima opcion es que quiera comer o mover sin comer, true ssi es
			// valido
			if (fichasPosiblesAComer.isEmpty()) {
				return estado.esMovimientoSimple(inicio, destino);
			} else {
				if (pertenceA(fichasPosiblesAComer, inicio))
					return estado.movimientoComerFicha(inicio, destino);
			}
			return false;
		}
	}

	private boolean pertenceA(List<Pair<Integer, Integer>> lista,
			Pair<Integer, Integer> par) {
		boolean encontre = false;
		int i;
		if (lista.isEmpty()) {
			return false;
		} else {
			i = 0;
			while (!encontre && i < lista.size()) {
				encontre = this.sonIgualesLosPares(lista.get(i), par);
				i++;
			}
		}
		return (encontre ? true : false);
	}

	private boolean sonIgualesLosPares(Pair<Integer, Integer> p1,
			Pair<Integer, Integer> p2) {
		return (p1.getFirstElem() == p2.getFirstElem() && p1.getSecondElem() == p2
				.getSecondElem());
	}

	/**
	 * @return el jugador actual no es humano : {@code boolean}
	 */
	private boolean juegaIA() {
		return !this.modelo.esHumano(this.modelo.colorJugador(turno));
	}

	/**
	 * Ejecuta el siguiente movimiento en caso de tratarse de un jugador no
	 * humano
	 */
	private void siguienteMovimiento() {
		boolean soyIA = juegaIA();
		boolean puedoMover = !this.modelo.juegoTermino();
		if (puedoMover) {
			if (soyIA) {
				// puede venir de una jugada de usuario
				// o de otra IA
				AiTask aiTask = new AiTask();
				aiTask.execute();
			}
		} else {
			this.jugando = false;
		}
	}

	/**
	 * Cambia el jugador actual (a quien le toca)
	 */
	private void cambiarJugadorActual() {
		this.modelo.obtenerEstado().changeLevel();
		this.modelo.update();
	}

	/**
	 * @return true si se est� jugando una partida : {@code boolean}
	 */
	public boolean partidaEnCurso() {
		return this.jugando;
	}

	/**
	 * Detiene la partida
	 */
	public void frenarPartida() {
		this.jugando = false;
		this.turno = 0;
		this.modelo.deshabilitarTablero();
		this.modelo.habilitarPreferencias();
	}

	public void cambiarNombreJugador(int jugador, String nuevoNombre) {
		this.modelo.definirNombre(jugador, nuevoNombre);
	}

	public void cambiarCerebroJugador(int jugador, boolean cerebro) {
		this.modelo.definirCerebro(jugador, cerebro);
	}

	public void cambiarColorJugador(int jugador, int nuevoColor) {
		this.modelo.definirColor(jugador, nuevoColor);
	}

	public String obtenerNombreJugador(int jugador) {
		return this.modelo.nombreJugador(this.modelo.colorJugador(jugador));
	}

	public boolean obtenerCerebroJugador(int jugador) {
		return !this.modelo.esHumano(this.modelo.colorJugador(jugador));
	}

	public int obtenerColorJugador(int jugador) {
		return this.modelo.colorJugador(jugador);
	}

	public int obtenerTurno() {
		return this.turno;
	}

	public void mostrarGUI() {
		this.modelo.mostrarGUI();
	}

	// TAREA QUE CORRE LA CONSULTA EN EL MOTOR

	private class AiTask extends SwingWorker<DamasEstado, Void> {
		@Override
		synchronized protected DamasEstado doInBackground() {
			modelo.interaccionTablero(false);
			return ia.nextMove(modelo.obtenerEstado());
		}

		@Override
		synchronized public void done() {
			try {
				wait(delay);
				modelo.definirEstado(get());
				if (!modelo.genereComidaObligatoria()) {
					turno = 1 - turno;
					if (!modelo.esHumano(modelo.ROJA)
							&& !modelo.esHumano(modelo.NEGRA)) {
						cambiarJugadorActual();
					}
				}

				modelo.update();
				if (juegaIA()) {
					siguienteMovimiento();
				}
			} catch (InterruptedException ignore) {
			} catch (java.util.concurrent.ExecutionException e) {
				String why = null;
				Throwable cause = e.getCause();
				if (cause != null) {
					why = cause.getMessage();
				} else {
					why = e.getMessage();
				}
				System.err.println("Error: " + why);
			}
		}
	}
}
